package com.fengye.security.config;

import com.fengye.security.custom.filter.JwtAuthorizationFilter;
import com.fengye.security.custom.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.List;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * @author sunlei
 * @version 1.0
 * @date 2023/9/8 17:13:05
 * @description 定义Spring Security配置类，
 * 在Spring Security 5.x及以上版本中，推荐使用SecurityFilterChain来配置过滤器链，配置类无需在继承自WebSecurityConfigurerAdapter，
 * 当然你也可以用继承WebSecurityConfigurerAdapter类的方式去实现配置类，【注意】这两种方式不可同时使用，否则会报错。
 *
 * Spring Security 5.7+ 及 Spring Boot 3.x 开始，弃用了 antMatchers 的旧式配置方式，推荐使用 Lambda DSL 或 新式链式调用。如果项目升级后依赖的 Spring Security 版本较高（如 6.x+），旧配置会因 API 移除而报错。
 */
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class SecurityConfig {

    @Autowired
    JwtAuthorizationFilter jwtAuthenticationTokenFilter;

    @Autowired
    AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    AccessDeniedHandler accessDeniedHandler;

    @Autowired
    CustomUserDetailsService userDetailsService;

    /**
     * 密码明文加密方式配置
     * @return BCryptPasswordEncoder对象
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 新版本默认方式
        return new BCryptPasswordEncoder();

        // 利用工厂类PasswordEncoderFactories实现，工厂类内部采用的是委派密码编码方案!
        // 推荐使用该方案,因为后期可以实现多密码加密方案共存效果!
        //return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    /**
     * 注入authenticationManager
     * @param authenticationConfiguration 认证配置类
     * @return 返回AuthenticationManager对象
     * @throws Exception 异常
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    /**
     * 配置httpSecurity
     * @param httpSecurity httpSecurity对象
     * @return 返回SecurityFilterChain安全过滤器链对象
     * @throws Exception 异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // CSRF禁用，因为不使用session
                .csrf(AbstractHttpConfigurer::disable)
                // 设置无状态会话
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 过滤请求
                // 配置请求授权
                .authorizeHttpRequests(auth -> auth
                        // 匿名访问接口（禁止已认证用户访问）
                        .requestMatchers("/user/login", "/user/reg").anonymous()
                        // 无条件放行接口
                        .requestMatchers(
                                "/article/updateViewCount/**",
                                "/resign/**",
                                "/swagger-ui/**",
                                "/swagger-resources/**",
                                "/v3/**",
                                "/actuator/**",
                                "/monitor/**"
                        ).permitAll()
                        // 放行静态资源
                        .requestMatchers(
                                "/**/*.css",
                                "/**/*.js",
                                "/**/*.html",
                                "/**/*.ico",
                                "/images/**",
                                "/bootstrap3/**",
                                "/uploads/**"
                        ).permitAll()
                        // 其他请求需要认证
                        .anyRequest().authenticated()
                )
                // 添加 JWT 过滤器
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                // 配置异常处理
                .exceptionHandling(exception -> exception
                        .authenticationEntryPoint(authenticationEntryPoint)
                        .accessDeniedHandler(accessDeniedHandler)
                )
                // 禁用注销功能
                .logout(AbstractHttpConfigurer::disable)

                // 启用 CORS（需配合 CorsConfigurationSource Bean）
                .cors(withDefaults())
                // 使用自定义 UserDetailsService
                .userDetailsService(userDetailsService);

        return httpSecurity.build();
    }

    /**
     * 配置CORS（跨源资源共享）策略，允许所有来源访问API接口
     * @return CorsConfigurationSource 返回配置好的CORS配置源对象
     */
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        /**
         * 创建并配置CORS策略参数：
         * - 允许所有来源访问
         * - 允许GET/POST/PUT/DELETE方法
         * - 允许所有请求头
         */
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(List.of("*"));
        config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
        config.setAllowedHeaders(List.of("*"));

        /**
         * 创建URL路径匹配的CORS配置源，并将配置应用到所有路径
         */
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}
